
/*
 * @项目名称: 模板开发
 * @FilePath: /meek/src/geometry/extent.js
 * @Author: 
 * @Date: 2017-03-23T11:12:12+08:00
 * @LastEditors: yangxianghong@datatang.com
 * @LastEditTime: 2023-07-13 16:18:51
 * Copyright (c) 2018-2022
 */

/**
 * Created by zypc on 2016/11/13.
 */

import Geometry from './geometry'
import {ExtentUtil} from './support/extentutil'
import { Style } from './../style/style';
import Feature from '../meek/feature';
import Line from './line';
import { distance, pointDistanceToSegment, getProjectionPointOnLine } from './support/geometryutil'

/**
 * 矩形类和数据结构
 *
 * @class Extent
 * @extends Geometry
 * @module geometry
 * @constructor
 * @example
 *
 *  var rect = new Datatang.Extent(10, 10, 500, 500)
 */
export default class Extent extends Geometry {

  /**
   * 构造函数，构建一个extent对象
   *
   * @param xmin {Number}
   * @param ymin {Number}
   * @param xmax {Number}
   * @param ymax {Number}
   *
   */
  constructor (xmin = 0, ymin = 0, xmax = 0, ymax = 0) {
    super()

    this._xmin = xmin
    this._ymin = ymin
    this._xmax = xmax
    this._ymax = ymax

    this._rings = []

    /**
     * 记录内点更新的次数
     * @type {number}
     * @private
     */
    this._flatInteriorPointRevision = -1

    /**
     * 缓存当前内点
     * @type {null}
     * @private
     */
    this._flatInteriorPoint = null
  }

  /**
   * 获取Geometry的类型
   *
   * @property geometryType
   * @returns {String}
   */
  get geometryType () { return Geometry.EXTENT }

  /**
   * 计算x轴的中心坐标
   *
   * @property centerX
   * @type {Number}
   */
  get centerX () { return (this.xmax + this.xmin) / 2 }

  /**
   * 计算Y轴的中心坐标
   *
   * @property centerY
   * @type {Number}
   */
  get centerY () { return (this.ymax + this.ymin) / 2 }

  /**
   * 计算最小外接矩形的宽
   *
   * @property width
   * @returns {Number}
   */
  get width () { return Math.abs(this.xmax - this.xmin) }

  /**
   * 计算最小外接矩形的高
   *
   * @property height
   * @returns {Number}
   */
  get height () { return Math.abs(this.ymax - this.ymin) }

  /**
   * 获取最小外接矩形本对象
   *
   * @property extent
   * @returns {Object} extent
   */
  get extent () { return this }

  /**
   * X 轴最小值
   *
   * @property xmin
   * @type {Number}
   */
  get xmin () { return this._xmin }
  set xmin (value) {
    this._xmin = value
    this.changed()
  }

  /**
   * Y 轴最小值
   *
   * @property ymin
   * @type {Number}
   */
  get ymin () { return this._ymin }
  set ymin (value) {
    this._ymin = value
    this.changed()
  }

  /**
   * X 轴最大值
   *
   * @property xmax
   * @type {Number}
   */
  get xmax () { return this._xmax }
  set xmax (value) {
    this._xmax = value
    this.changed()
  }

  /**
   * X 轴最大值
   *
   * @property ymax
   * @type {Number}
   */
  get ymax () { return this._ymax }
  set ymax (value) {
    this._ymax = value
    this.changed()
  }


  /**
   * 判断点是否在矩形内
   *
   * @method containsXY
   * @param x {Number}
   * @param y {Number}
   */
  containsXY (x, y) {
    return ExtentUtil.containsPoint(this, [x, y])
  }

  /**
   * getFlatInteriorPoint
   * @returns {[*,*]}
   */
  getFlatInteriorPoint () {
    if (this._flatInteriorPointRevision !== this.revision) {
      this._flatInteriorPointRevision = this.revision
      //this._flatInteriorPoint = [this.centerX, this.centerY]
      this._flatInteriorPoint = [this.xmin, this.ymin]
    }

    return this._flatInteriorPoint
  }

  /**
   * 移动矩形通过x，y值
   *
   * @method move
   * @param x {Number}
   * @param y {Number}
   */
  move (x = 0, y = 0, opts) {
    let beyond
    if (opts) {
      if (opts.beyond) {
        beyond = opts.beyond
      }
    }

    let minPoint = [this.xmin,this.ymin]
    const width = this.width
    const height = this.height

    const newMinPoint = new Array(2)

    newMinPoint[0] = minPoint[0] + x
    newMinPoint[1] = minPoint[1] + y

    if (beyond) {
      if (newMinPoint[0] < beyond.xmin) {
        newMinPoint[0] = beyond.xmin
      }

      if (newMinPoint[0] + width >= beyond.xmax) {
        newMinPoint[0] = beyond.xmax - width
      }

      if (newMinPoint[1] < beyond.ymin) {
        newMinPoint[1] = beyond.ymin
      }

      if (newMinPoint[1] + height >= beyond.ymax) {
        newMinPoint[1] = beyond.ymax - height
      }
    }

    const newCoordiante = [
      newMinPoint,
      [newMinPoint[0] + width, newMinPoint[1]],
      [newMinPoint[0] + width, newMinPoint[1] + height],
      [newMinPoint[0], newMinPoint[1] + height],
      newMinPoint]

    this.setCoordinates(newCoordiante)
  }

  /**
   * 设置多边形的边，如果设置了边，则需要重新计算
   * 外接矩形
   *
   * @method rings
   * @returns {Array}
   */
  get rings () {
    if (this._rings.length === 0) {
      if (!isNaN(this.xmin) && !isNaN(this.ymin) && !isNaN(this.xmax) && !isNaN(this.ymax)) {
        this._rings = ExtentUtil.minMaxToRing(this.xmin, this.ymin, this.xmax, this.ymax)
      }
    }

    return this._rings
  }

  set rings (value) {
    this._rings = value
    let extentArr = ExtentUtil.boundingSimpleExtent(value)
    this.xmin = extentArr[0]
    this.ymin = extentArr[1]
    this.xmax = extentArr[2]
    this.ymax = extentArr[3]
  }

  getArea() {
    return this.width * this.height;
  }

  /**
   * 获取图形的坐标数据
   *
   * @method getCoordinates
   * @returns {[*,*]}
   */
  getCoordinates () {
    if (this._rings.length === 0) {
      if (!isNaN(this.xmin) && !isNaN(this.ymin) && !isNaN(this.xmax) && !isNaN(this.ymax)) {
        this._rings = ExtentUtil.minMaxToRing(this.xmin, this.ymin, this.xmax, this.ymax)
      }
    }

    return this._rings
  }

  /**
   * 根据得到的坐标点，计算出表单显示的位置
   *
   * @param {Number} offsetX x的偏移量
   * @param {Number} offsetY y的偏移量
   * @returns {[*,*]}
   */
  getFormShowPosition (offsetX = 0, offsetY = 0) {
    return this.rings[1];
  }

  /**
   * 设置坐标数据
   *
   * @method setCoordinates
   * @param coordinates
   */
  setCoordinates (coordinates) {
    this.rings = coordinates

    let extentArr = ExtentUtil.boundingSimpleExtent(coordinates)
    this.xmin = extentArr[0]
    this.ymin = extentArr[1]
    this.xmax = extentArr[2]
    this.ymax = extentArr[3]

    this.changed()
  }

  /**
   * 获取点位于图形坐标点的下标
   *
   * @method getCoordinateIndex
   * @param coord {Array}
   * @returns {Number}
   */
  getCoordinateIndex (coord) {
    return this.getCoordinates().findIndex( points =>
      points[0] === coord[0] && points[1] === coord[1]
    )
  }

  /**
   * 克隆矩形框
   *
   * @method clone
   * @returns {Extent}
   */
  clone () {
    return new Extent(this.xmin, this.ymin, this.xmax, this.ymax)
  }

  getRefLineCoords(extent) {
    let coordinates = this.getCoordinates();
    let y;
    let flat = coordinates.map(item => item[1]);
    y = Math.min.apply(null, flat);
    if (extent) {
      return [[extent[0], y], [extent[2], y]];
    } else {
      return [[-99999, y], [99999, y]];
    }
  }

  getRefLine(extent, style) {
    const coords = this.getRefLineCoords(extent);
    console.log(coords);
    this._referLineFeature = new Feature(new Line());
    this._referLineFeature.geometry.setCoordinates(coords);
    if (style) {
      this._referLineFeature.style = style;
    } else {
      this._referLineFeature.style = this.getDefaultStyleFunction()(this._referLineFeature);
      this._referLineFeature.style.splice(-1);
      this._referLineFeature.style.forEach(s => {
        s.style = "dash";
        s.color = [76, 189, 255];
      });
    }
    return this._referLineFeature;
  }

  /**
   * 获取默认绘制样式
   *
   * @method getDefaultStyleFunction
   * @returns {Function}
   */
  getDefaultStyleFunction() {
    const styles = Style.createDefaultEditing()
    return function (feature) {
      return styles[feature.geometry.geometryType]
    }
  }

  /**
   * 自动吸附
   * @param {*} x x坐标
   * @param {*} y y坐标
   * @param {*} tolerance 吸附范围
   * @param {*} type 吸附类型 1顶点 2边缘
   */
  intersectPoint(x, y, tolerance = 3, type = 1) {
    // 吸附顶点
    if(type === 1) {
      const point = this.rings.find(item => {
        const pointDistance = distance([x,y], item)
        if (pointDistance <= tolerance) {
          return true
        }
      })
      return point;
    }
    // 吸附边缘
    if(type === 2) {
      let i = 0;
      let point = null;
      let min = tolerance;
      const rings = this.rings;
      while(i < rings.length - 1) {
        // 点到线的距离
        const distance = pointDistanceToSegment([x, y], [rings[i], rings[i + 1]]);
        if(distance <= tolerance && distance <= min) {
          min = distance;
          // 点的投影
          point = getProjectionPointOnLine(rings[i], rings[i + 1], [x, y]);
        }
        i++;
      }
      return point;

      // const distances = [Math.abs(this.ymin - y), Math.abs(this.xmax - x), Math.abs(this.ymax - y), Math.abs(this.xmin - x)];
      // const min = Math.min(...distances);
      // if(min <= tolerance) {
      //   const index = distances.findIndex(i => i == min);
      //   // 0 上
      //   if(index === 0) {
      //     return [x, this.ymin]
      //   }
      //   // 1 右
      //   if(index === 1) {
      //     return [this.xmax, y]
      //   }
      //   // 2 下
      //   if(index === 2) {
      //     return [x, this.ymax]
      //   }
      //   // 3 左
      //   if(index === 3) {
      //     return [this.xmin, y]
      //   }
      // }
    }
  }
}

